package com.beyondsoft.sabg.edu.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.beyondsoft.sabg.edu.model.db.core.BasicEntity;
import com.beyondsoft.sabg.edu.model.db.core.BinaryFile;
import com.beyondsoft.sabg.edu.model.exception.InvalidParamException;
import com.beyondsoft.sabg.edu.model.vo.core.PageableResponseVO;
import com.beyondsoft.sabg.edu.repository.core.BinaryFileRepository;
import com.beyondsoft.sabg.edu.repository.core.SimpleRepository;
import com.beyondsoft.sabg.edu.service.SimpleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;
import java.util.stream.Collectors;

/**
 * service 通用逻辑和方法
 *
 * @Author Mo cr
 * @Date 2023/8/7
 */
@Slf4j
public abstract class AbstractSimpleServiceImpl<T extends BasicEntity, R extends SimpleRepository<T>> implements SimpleService<T, R> {
    @Autowired
    private R simpleRepo;
    @Autowired
    private BinaryFileRepository binaryFileRepository;

    public R getRepo() {
        return simpleRepo;
    }

    @Override
    public List<T> findAll() {
        return simpleRepo.findAllByValidity(true);
    }

    @Override
    public Page<T> findAll(Pageable p) {
        return simpleRepo.findAllByValidity(p, true);
    }

    @Override
    public PageableResponseVO findAll(Object o, Pageable p) {
        Specification<T> queryCondition = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            addConditions(root, criteriaBuilder, predicates, o);
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<T> page = simpleRepo.findAll(queryCondition, p);

        return PageableResponseVO.builder()
                .totalElements(page.getTotalElements())
                .totalPages(page.getTotalPages())
                .data(transferDTO(page))
                .build();
    }

    @Override
    public Optional<T> findById(Long id) {
        return simpleRepo.findByIdAndValidity(id, true);
    }

    @Override
    public List<T> findAllByIds(Iterable<Long> ids) {
        return simpleRepo.findAllByIdInAndValidity(ids, true);
    }

    @Override
    public void deleteByIds(List<Long> ids) {
        simpleRepo.deleteInBatch(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T save(T t, Object o) throws InvalidParamException {
        checkSaveParams(o);
        saveBeforeProcess(o, t);
        T saved = simpleRepo.save(t);
        savePostProcess(o, saved);
        return saved;
    }

    @Override
    public List<T> batchSave(T t, Object o) throws InvalidParamException {
        checkSaveParams(o);
        List<T> ts = toBatchEntity(new ArrayList<T>(), o);
        saveBeforeProcess(o, ts);
        List<T> saved = simpleRepo.saveAll(ts);
        savePostProcess(o, saved);
        return saved;
    }

    /**
     * 接入该方法需要重写 updateBeforeProcess 方法，否则会抛异常
     *
     * @param o
     */
    @Override
    public void batchUpdate(Object o) {
        checkUpdateParams(o);
        List<T> ts = updateBeforeProcess(o);
        List<T> saved = simpleRepo.saveAll(ts);
        updatePostProcess(saved);
    }

    /**
     * 批量保存时，把DTO里的数据处理成多个实体类
     *
     * @param ts 实体类列表
     * @param o  接口传参dto
     * @return
     */
    protected List<T> toBatchEntity(ArrayList<T> ts, Object o) {
        return ts;
    }

    protected void saveBeforeProcess(Object o, T t) {
        CopyOptions options = CopyOptions.create()
                .setIgnoreNullValue(true)  // 忽略源对象属性为空的情况
                .setIgnoreError(true);     // 忽略复制过程中出现的错误
        BeanUtil.copyProperties(o, t, options);
    }

    protected void saveBeforeProcess(Object o, List<T> t) {
        BeanUtil.copyProperties(o, t);
    }

    protected void savePostProcess(Object o, T t) {

    }

    protected void savePostProcess(Object o, List<T> ts) {

    }

    /**
     * 必须要重写 否则会抛异常
     *
     * @param o
     * @return 从数据库查出的实体
     */
    protected List<T> updateBeforeProcess(Object o) {
        return null;
    }

    protected void updatePostProcess(Object o) {
    }

    /**
     * 校验save接口传参，若校验失败，直接抛出异常
     *
     * @param o 接口传递对象
     */
    protected void checkSaveParams(Object o) throws InvalidParamException {
        if (o == null) {
            throw new InvalidParamException("request params is null");
        }
    }

    /**
     * 校验update接口传参，若校验失败，直接抛出异常
     *
     * @param o 接口传递对象
     */
    protected void checkUpdateParams(Object o) throws InvalidParamException {
        if (o == null) {
            throw new InvalidParamException("request params is null");
        }
    }


    protected void addConditions(Root<T> root, CriteriaBuilder criteriaBuilder, List<Predicate> predicates, Object o) {
        predicates.add(criteriaBuilder.equal(root.get("validity"), true));
    }

    /**
     * 分页查询后，转换成DTO
     *
     * @param page
     * @return
     */
    protected Object transferDTO(Page<T> page) {
        return page.stream().collect(Collectors.toList());
    }

    protected Set<BinaryFile> saveImage(Object o, T t) {
        return new HashSet<>();
    }




}
